#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
@File Name  : Mzitu_3.0.py
@Author     : LeeCQ
@Date-Time  : 2019/12/4 15:32

3.0  从基类重写，
能引用 不继承；
"""
import re
import sys, logging

logger = logging.getLogger("Mzitu_3")  # 创建实例
formatter = logging.Formatter("[%(asctime)s] < %(threadName)s: %(thread)d > [%(levelname)s] %(message)s")
# 终端日志
consle_handler = logging.StreamHandler(sys.stdout)
consle_handler.setFormatter(formatter)  # 日志文件的格式
logger.setLevel(logging.DEBUG)  # 设置日志文件等级
logger.addHandler(consle_handler)  #

from SQL.MySQL import MyMySqlAPI
from bs4 import BeautifulSoup

import requests, urllib3

import os, sys, traceback
import time


class MzituDB(MyMySqlAPI):
    def __init__(self, **kwargs):
        super().__init__(host='t.sql.leecq.xyz',
                         port=10080,
                         user='Mzitu',
                         passwd='Mzitu123456',
                         db='Mzitu',
                         charset='GB18030',
                         **kwargs
                         )


class Mzitu:
    """基类：

    这个类包含了所有的函数，但是不要有控制函数
    """

    def __init__(self, timeout=10, retry=5, debug_level=0,
                 log_root=None, album_root=None
                 ):
        self.__sql = MzituDB()
        self.sheet_all_album = 'MzituList'
        self._timeout = timeout
        self._retry = retry
        self.debug_level = debug_level
        self.log_root = log_root or './sup'
        self.album_root = album_root or './sup/albums'

        os.makedirs(self.log_root, exist_ok=True)
        os.makedirs(self.album_root, exist_ok=True)

    def sql_pooling(self):
        """启用数据库链接池"""
        self.__sql.pooling_sql()

    def sql_create_table_one_album(self, name):
        command = (f"CREATE TABLE IF NOT EXISTS `{self.sheet_all_album}_{name}` (\n"
                   " ID         INT             UNSIGNED  AUTO_INCREMENT unique,"  # ID计数 
                   " IDD        INT             NOT NULL    ,"  # 同易相册的IDD因该相同。
                   " PHOTO_NAME VARCHAR(100)    NOT NULL    unique,"  # 照片的的名字
                   " PHOTO_URI  VARCHAR(200)    NOT NULL    ,"
                   " PHOTO_URL  VARCHAR(200)    NOT NULL    ,"  # 照片在Mzitu的绝对URL
                   " LOCAL_PATH VARCHAR(200)                ,"
                   " PHOTO_DATA MEDIUMBLOB                  ,"  # 保存相册内部的数据 - 每张照片的url - 
                   " PRIMARY KEY ( IDD, PHOTO_NAME )"
                   ")")
        return self.__sql.create_table(command)

    def sql_create_table_albums_list(self):
        command = (f"CREATE TABLE IF NOT EXISTS `{self.sheet_all_album}` ( "
                   # 字段的设置 - 
                   " ID         INT             UNSIGNED  AUTO_INCREMENT, "  # ID计数 
                   " IDD        INT        NOT NULL    unique, "  # IDD 相册图 标识码
                   " DOWNLOAD   ENUM('N','Y')  NULL DEFAULT 'N', "
                   " ALBUM_NAME VARCHAR(999)  NOT NULL ,  "  # 相册的名字
                   " ALBUM_URL  VARCHAR(999)  NOT NULL , "  # 相册在Mzitu的URL
                   " TIME       DATETIME, "
                   " TAGS       CHAR(10), "
                   " DATA       JSON, "  # 保存相册内部的数据 - 每张照片的url - 
                   " PRIMARY KEY ( IDD )"
                   ")")
        return self.__sql.create_table(command)

    def _write_Error_log(self, model_name):
        file_name = self.log_root + model_name + '.log'
        if self.debug_level <= 2:
            with open(file_name, 'a', encoding='gbk') as log:
                log.write('\n' +
                          f'  {time.strftime("%Y-%m-%d %H:%M:%S")}  '.center(50, '*') +
                          '\n')
            traceback.print_exc(file=file_name)
        if self.debug_level <= 1:
            traceback.print_exc()
        if self.debug_level <= 0:
            pass
        if self.debug_level >= 3:
            raise

    def get_http(self, _url, _headers, timeout=None, except_status_code=200, **kwargs):
        """requests get的统一入口"""
        _timeout = timeout or self._timeout
        _headers.setdefault('Host', _url.split('/')[2])
        _headers.setdefault('User-Agent',
                            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18362')

        _n, _s = self._retry, time.time()
        while _n:
            _n -= 1
            try:
                urllib3.disable_warnings()
                response = requests.get(_url, _headers, timeout=_timeout, verify=False, **kwargs)  # get方法
                if response.status_code == except_status_code:  # 查看HTTP状态
                    return time.time() - _s, response
                else:
                    if self.debug_level > 1:
                        logger.warning(f'HTTP({_url})返回异常: {response.status_code}')
            except requests.exceptions.ConnectTimeout:
                logger.error(f'HTTP({_url})返回异常: 连接超时')
            except:
                logger.error(f'HTTP({_url})返回异常: 未知错误')
                self._write_Error_log('HTTPError')

        logger.error(f'HTTP({_url})重试次数用尽...')
        return -1, 'Error Connect ' + _url

    def save_photo2local(self, dir_name, file_name, data):
        """保存一张图片到本地。

        :return: 0 OR Error
        """
        file_path = '/'.join((self.album_root, dir_name, file_name))
        with open(file_path, 'wb') as f:
            f.write(data)
        return 0

    def save_photo2sql(self, table_name, idd, data):
        """保存一张图片到数据库"""
        return self.__sql.update(table_name, 'IDD', idd, PHOTO_DATA=data)

    def download_one_photo(self, _url, referer=None):
        """下载一张照片到内存，并返回数据

        :return 数据字节
        """
        header = {'Referer': referer or 'https://www.mzitu.com/', }
        _, data = self.get_http(_url, header, allow_redirects=False)
        if _ > 0:
            return data.content
        else:
            return 'Error'

    @staticmethod
    def _re_albumsList(text):
        """解析相册列表

        :param text:
        :return:
        """
        idd, url, name = [], [], []
        soup = BeautifulSoup(text, 'lxml')
        for href, _text in [(url.get('href'), url.text) for url in soup.select('p.url > a')][::-1]:
            idd.append(href.split('/')[-1])
            url.append(href)
            name.append(_text)
        return idd, url, name

    def get_albums_list2sql(self, _url):
        """获取相册列表，并上传到数据库。

        ！倒序上传！
        键值参见：self._create_table_albums_list(self):
        :return int(数据长度), 相册列表
        """
        header = {'Referer': 'https://www.mzitu.com/', }
        _, page = self.get_http(_url, header)
        if _ > 0:
            idds, urls, names = self._re_albumsList(page.text)
            _r = self.__sql.insert(self.sheet_all_album, ignore=True,
                                   IDD=idds, ALBUM_NAME=names, ALBUM_URL=urls)
            return _r, [(i, u, n) for i, u, n in zip(idds, urls, names)]
        else:
            return -1, []

    def _re_photoList(self, text):
        """解析图片列表

        :return
        """
        soup = BeautifulSoup(text, 'lxml')

        title = soup.select('h2.main-title')[0].text  # 获得套图的标题[0]
        tags = soup.select('div.main-meta > span')[0].text.replace('分类：', '').strip()  # 获得分类
        times = soup.select('div.main-meta > span')[1].text.replace('发布于', '').strip()  # 获得发布时间
        page_num = soup.select('div.pagenavi > a')[-2].text  # 获得页码

        album_url = soup.select('div.pagenavi > a')[-2].get('href')
        album_url = album_url[:album_url.rindex('/')]

        pic_page = [album_url + '/' + str(num) for num in range(1, int(page_num) + 1)]  # 获取图片每个页面的URL，装入列表

        images_model = soup.select('div.main-image > p > a > img')[0].get('src')
        images = [images_model[:-6] + str(num).rjust(2, '0') + '.jpg' for num in range(1, int(page_num) + 1)]
        # 获取每张图片的URL，装入列表
        data = {
            'idd': album_url.split('/')[],
            'title': re.sub(r'[\.:*?"<> |]', '-', title),
            'times': times,
            'tags': tags,
            'pagePhoto': pic_page,
            'images': images,
            }
        return data

    def get_photo_list2sql(self, _url):
        """获取相册中的图片列表，并上传到数据库

        键值参见：self._create_table_one_album(self, name):
        +++++ 更新albums_list - data, tags, time 字段  +++++
        :param _url:
        :return: 数据长度，图片列表
        """
        header = {'Referer': 'https://www.mzitu.com/', }
        _, page = self.get_http(_url, header)
        if _ > 0:
            _ = self._re_photoList(page.text)
